package labox.innovation.loginserver;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javolution.util.FastMap;
import labox.innovation.config.Config;
import labox.innovation.db.core.L2DatabaseFactory;
import labox.innovation.loginserver.GameServerTable.GameServerInfo;
import labox.innovation.loginserver.socket.LoginClient;
import labox.innovation.loginserver.socket.serverpackets.ResponseBindAccount;
import labox.innovation.loginserver.socket.serverpackets.ResponseChangePassWord;
import labox.innovation.loginserver.socket.serverpackets.LoginFail.LoginFailReason;
import labox.innovation.loginserver.svn.Svn;
import labox.innovation.util.Rnd;
import labox.innovation.util.Util;
import labox.innovation.util.crypt.Base64;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class ...
 * 
 * @version $Revision: 1.7.4.3 $ $Date: 2005/03/27 15:30:09 $
 */
public class LoginController {
	protected static final Logger _log = LoggerFactory.getLogger(LoginController.class.getName());

	private static LoginController _instance = new LoginController();

	private final static int LOGIN_TIMEOUT = Config.LOGIN_TIMEOUT;

	/**
	 * 在登录服务器上还没有得到授权的客户端连接集合
	 */
	protected Queue<LoginClient> _clients = new ConcurrentLinkedQueue<LoginClient>();

	/**
	 * 在登录服务器上已授权的客户端连接集合
	 */
	protected Map<String, Client> _loginServerClients = new ConcurrentHashMap<String, Client>();

	/**
	 * 禁止连接的IP列表
	 */
	private Map<String, BanInfo> _bannedIps = new FastMap<String, BanInfo>().shared();

	private Map<String, BanInfo> _releasedIps = new FastMap<String, BanInfo>().shared();

	/**
	 * 洪水保护记录表
	 */
	private Map<InetAddress, FailedLoginAttempt> _hackProtection;

	public static enum AuthLoginResult {

		/**
		 * 无效的密码
		 */
		INVALID_PASSWORD,

		/**
		 * 账户被禁止
		 */
		ACCOUNT_BANNED,

		/**
		 * 账户已在登陆服务器上授权
		 */
		ALREADY_ON_LS,

		/**
		 * 账户已在游戏服务器上登录
		 */
		ALREADY_ON_GS,

		/**
		 * 授权成功
		 */
		AUTH_SUCCESS
	}

	protected byte[][] _blowfishKeys;

	private static final int BLOWFISH_KEYS = 20;

	//检查注册的账户名称是否符合标准的正则表达式
	private static Pattern NAME_PATTERN;

	//检查注册的账户密码是否符合标准的正则表达式
	private static Pattern PASSWORD_PATTERN;

	/**
	 * 电话号码正则表达式
	 */
	private static Pattern PHONENO_TEMPLATE;

	/**
	 * 版本更新控制器
	 */
	private Svn _svn;

	public static LoginController getInstance() {
		return _instance;
	}

	private LoginController() {
		_log.info("Loading LoginController...", Config.SERVER_ID);

		_hackProtection = new FastMap<InetAddress, FailedLoginAttempt>();

		//设置账户的正则
		setNamePattern(Config.ACCOUNT_NAME_TEMPLATE);

		//设置密码的正则
		setPasswordPattern(Config.ACCOUNT_PASSWORD_TEMPLATE);

		//开启清理超时死连接服务
		PurgeThread purgeThread = new PurgeThread();
		purgeThread.setDaemon(true);
		purgeThread.setPriority(Thread.MIN_PRIORITY);
		purgeThread.start();
	}

	/**
	 * @return Returns a random key
	 */
	public byte[] getBlowfishKey() {
		return _blowfishKeys[(int) (Math.random() * BLOWFISH_KEYS)];
	}

	public void addLoginClient(LoginClient client) {
		_clients.add(client);
	}

	public void removeLoginClient(LoginClient client) {
		_clients.remove(client);
	}

	public SessionKey assignSessionKeyToClient(String account, Client client) {
		SessionKey key;

		key = new SessionKey(Rnd.nextInt(), Rnd.nextInt(), Rnd.nextInt(), Rnd.nextInt());
		_loginServerClients.put(account, client);
		return key;
	}

	public void removeAuthedLoginClient(String account) {
		_loginServerClients.remove(account);
	}

	public boolean isAccountInLoginServer(String account) {
		return _loginServerClients.containsKey(account);
	}

	public Client getAuthedClient(String account) {
		return _loginServerClients.get(account);
	}

	/**
	 * 校验登陆玩家用账户信息
	 * 
	 * @param account
	 * @param password
	 * @param client
	 * @return
	 * @throws HackingException
	 */
	public AuthLoginResult tryAuthLogin(String account, String password, Client client) throws HackingException {

		//默认是无效的密码
		AuthLoginResult ret = AuthLoginResult.INVALID_PASSWORD;
		// check auth 检查权限
		if (loginValid(account, password, client)) {
			// 默认登录了游戏服务器
			ret = AuthLoginResult.ALREADY_ON_GS;
			if (!isAccountInAnyGameServer(account)) {//检查是否登录了任何一个游戏服务器
				//把客户端放入已授权池
				_loginServerClients.put(account, client);
				ret = AuthLoginResult.AUTH_SUCCESS;
				//未授权池中移除
				removeLoginClient((LoginClient) client);
			}
		} else {
			if (client.getAccessLevel() < 0) {
				ret = AuthLoginResult.ACCOUNT_BANNED; //-- 屏蔽
			}
		}

		return ret;
	}

	/**
	 * 增加一个地址和超时时间到登陆服务器的禁止列表中.
	 * 
	 * @param address
	 *            The Address to be banned.
	 * @param expiration
	 *            Timestamp in miliseconds when this ban expires
	 * @throws UnknownHostException
	 *             if the address is invalid.
	 */
	public void addBanForAddress(String address, long expiration) throws UnknownHostException {
		InetAddress netAddress = InetAddress.getByName(address);
		if (!_bannedIps.containsKey(netAddress.getHostAddress()))
			_bannedIps.put(netAddress.getHostAddress(), new BanInfo(netAddress, expiration));
	}

	/**
	 * 增加一个地址和超时周期时间到登陆服务器的禁止列表中.
	 * 
	 * @param address
	 *            The Address to be banned.
	 * @param duration
	 *            is miliseconds
	 */
	public void addBanForAddress(InetAddress address, long duration) {
		if (!_bannedIps.containsKey(address.getHostAddress()))
			_bannedIps.put(address.getHostAddress(), new BanInfo(address, System.currentTimeMillis() + duration));
	}

	/**
	 * 增加一个地址和超时时间到登陆服务器的放行列表中.
	 */
	public void addReleasedForAddress(String address, long duration) throws UnknownHostException {
		InetAddress netAddress = InetAddress.getByName(address);
		if (!_releasedIps.containsKey(netAddress.getHostAddress()))
			_releasedIps.put(netAddress.getHostAddress(), new BanInfo(netAddress, System.currentTimeMillis() + duration));
		Config.RELEASELIST = true;
	}

	public boolean acceptAddress(InetAddress address) {
		if (Config.RELEASELIST) {
			return isReleasedAddress(address);
		} else {
			return !isBannedAddress(address);
		}
	}

	/**
	 * 判断是否被屏蔽
	 */
	public boolean isBannedAddress(InetAddress address) {
		String[] parts = address.getHostAddress().split("\\.");
		BanInfo bi = _bannedIps.get(address.getHostAddress());
		if (bi == null)
			bi = _bannedIps.get(parts[0] + "." + parts[1] + "." + parts[2] + ".0");
		if (bi == null)
			bi = _bannedIps.get(parts[0] + "." + parts[1] + ".0.0");
		if (bi == null)
			bi = _bannedIps.get(parts[0] + ".0.0.0");
		if (bi != null) {
			if (bi.hasExpired()) {
				_bannedIps.remove(address.getHostAddress());
				return false;
			} else {
				return true;
			}
		}
		return false;
	}

	public boolean isReleasedAddress(InetAddress address) {
		String[] parts = address.getHostAddress().split("\\.");
		BanInfo bi = _releasedIps.get(address.getHostAddress());
		if (bi == null)
			bi = _releasedIps.get(parts[0] + "." + parts[1] + "." + parts[2] + ".0");
		if (bi == null)
			bi = _releasedIps.get(parts[0] + "." + parts[1] + ".0.0");
		if (bi == null)
			bi = _releasedIps.get(parts[0] + ".0.0.0");
		if (bi != null) {
			if (bi.hasExpired()) {
				_releasedIps.remove(address.getHostAddress());
				return false;
			} else {
				return true;
			}
		}
		return false;
	}

	public Map<String, BanInfo> getBannedIps() {
		return _bannedIps;
	}

	public Map<String, BanInfo> getReleasedIps() {
		return _releasedIps;
	}

	/**
	 * 根据InetAddress取消屏蔽
	 * 
	 * @param address
	 *            The address to be removed from the ban list
	 * @return true if the ban was removed, false if there was no ban for this ip
	 */
	public boolean removeBanForAddress(InetAddress address) {
		return _bannedIps.remove(address.getHostAddress()) != null;
	}

	/**
	 * 根据地址从IP屏蔽列表中移除
	 * 
	 * @param address
	 *            The address to be removed from the ban list
	 * @return true if the ban was removed, false if there was no ban for this ip or the address was invalid.
	 */
	public boolean removeBanForAddress(String address) {
		try {
			return removeBanForAddress(InetAddress.getByName(address));
		} catch (UnknownHostException e) {
			return false;
		}
	}

	/**
	 * 根据InetAddress取消放行
	 * 
	 * @param address
	 *            The address to be removed from the ban list
	 * @return true if the ban was removed, false if there was no ban for this ip
	 */
	public boolean removeReleasedForAddress(InetAddress address) {
		boolean ok = _releasedIps.remove(address.getHostAddress()) != null;
		if (_releasedIps.size() == 0) {//如果不在有白名单，则关闭白名单的使用
			Config.RELEASELIST = false;
		}
		return ok;
	}

	/**
	 * 根据地址从IP放行列表中移除
	 * 
	 * @param address
	 *            The address to be removed from the ban list
	 * @return true if the ban was removed, false if there was no ban for this ip or the address was invalid.
	 */
	public boolean removeReleasedForAddress(String address) {
		try {
			return removeReleasedForAddress(InetAddress.getByName(address));
		} catch (UnknownHostException e) {
			return false;
		}
	}

	/**
	 * 通过账户名获取客户端对象的加密Key
	 * 
	 * @param account
	 * @return
	 */
	public int getKeyForAccount(String account) {
		Client client = _loginServerClients.get(account);
		if (client != null) {
			return client.getSessionKey();
		}
		return -1;
	}

	/**
	 * 通过账户名获取客户端对象的渠道ID
	 * 
	 * @param account
	 * @return
	 */
	public String getChannelIDForAccount(String account) {
		Client client = _loginServerClients.get(account);
		if (client != null) {
			return ((LoginClient) client).getChannelId();
		}
		return "-1";
	}

	/**
	 * 根据服务器ID获取该GS上在线玩家数量
	 * 
	 * @param serverId
	 * @return
	 */
	public int getOnlinePlayerCount(int serverId) {
		GameServerInfo gsi = GameServerTable.getInstance().getRegisteredGameServerById(serverId);
		if (gsi != null && gsi.isAuthed()) {
			return gsi.getCurrentPlayerCount();
		}
		return 0;
	}

	/**
	 * 判断登陆玩家是否在游戏服务器上，只要在任意游戏服上都返回true。
	 * 
	 * @param account
	 * @return
	 */
	public boolean isAccountInAnyGameServer(String account) {
		Collection<GameServerInfo> serverList = GameServerTable.getInstance().getRegisteredGameServers().values();
		for (GameServerInfo gsi : serverList) {
			GameServerThread gst = gsi.getGameServerThread();
			if (gst != null && gst.hasAccountOnGameServer(account)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 根据玩家账户获取当前登录游戏服相关信息
	 * 
	 * @param account
	 * @return
	 */
	public GameServerInfo getAccountOnGameServer(String account) {
		Collection<GameServerInfo> serverList = GameServerTable.getInstance().getRegisteredGameServers().values();
		for (GameServerInfo gsi : serverList) {
			GameServerThread gst = gsi.getGameServerThread();
			if (gst != null && gst.hasAccountOnGameServer(account)) {
				return gsi;
			}
		}
		return null;
	}

	/**
	 * 获取在线玩家数量 指的是所有游戏服上玩家总和
	 * 
	 * @return
	 */
	public int getTotalOnlinePlayerCount() {
		int total = 0;
		Collection<GameServerInfo> serverList = GameServerTable.getInstance().getRegisteredGameServers().values();
		for (GameServerInfo gsi : serverList) {
			if (gsi.isAuthed()) {
				total += gsi.getCurrentPlayerCount();
			}
		}
		return total;
	}

	/**
	 * 获取最大在线玩家允许数量，每个游戏服务器有最大上限设置。
	 * 
	 * @param id
	 *            游戏服务器ID
	 * @return
	 */
	public int getMaxAllowedOnlinePlayers(int id) {
		GameServerInfo gsi = GameServerTable.getInstance().getRegisteredGameServerById(id);
		if (gsi != null) {
			return gsi.getMaxPlayers();
		}
		return 0;
	}

	/**
	 * 账户级别设置
	 * 
	 * @param account
	 * @param banLevel
	 */
	public void setAccountAccessLevel(String account, int banLevel) {
		Connection con = null;
		PreparedStatement statement = null;
		try {
			con = L2DatabaseFactory.getInstance().getConnection();

			String stmt = "UPDATE accounts SET accessLevel=? WHERE login=?";
			statement = con.prepareStatement(stmt);
			statement.setInt(1, banLevel);
			statement.setString(2, account);
			statement.executeUpdate();
			statement.close();
		} catch (Exception e) {
			_log.error("Could not set accessLevel: ", Config.SERVER_ID, e);
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}
	}

	/**
	 * 判断登陆账户是否是Game Manager账户，这个方法写法有些问题：在等级判断上可以放到sql中进行处理，放到内存中遍历的话影响效率，多占用内存。
	 * 
	 * @param user
	 * @return
	 */
	public boolean isGM(String user) {
		boolean ok = false;
		Connection con = null;
		PreparedStatement statement = null;
		try {
			con = L2DatabaseFactory.getInstance().getConnection();
			statement = con.prepareStatement("SELECT accessLevel FROM accounts WHERE login=?"); //-- 这里条件可以下死一点。
			statement.setString(1, user);
			ResultSet rset = statement.executeQuery();
			if (rset.next()) {
				int accessLevel = rset.getInt(1);
				if (accessLevel > 0) {
					ok = true;
				}
			}
			rset.close();
			statement.close();
		} catch (Exception e) {
			_log.error("could not check gm state:" + e, Config.SERVER_ID, e);
			ok = false;
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}
		return ok;
	}

	/**
	 * 根据账户判断账户是否已经存在
	 * 
	 * @param userName
	 * @return
	 */
	public boolean isAccountExists(String userName) {
		boolean result = true;
		Connection connection = null;
		PreparedStatement statement = null;
		try {
			connection = L2DatabaseFactory.getInstance().getConnection();
			statement = connection.prepareStatement("select login from accounts where login = ?");
			statement.setString(1, userName);
			ResultSet rs = statement.executeQuery();
			result = rs.next();
			statement.close();
		} catch (Exception e) {
			_log.error("could not check is user exsits!", Config.SERVER_ID, e);
		} finally {
			try {
				if (connection != null)
					connection.close();
			} catch (Exception e) {
			}
		}
		return result;
	}

	/**
	 * 注册账户方法
	 * 
	 * @param userName
	 * @param passWord
	 * @param lastIp
	 * @return
	 */
	public boolean registerAccount(String userName, String passWord, String lastIp, byte sdk, String channelID, String clientInfo, int clientVersion) {
		boolean result = false;
		Connection connection = null;
		try { //-- 用户口令需要进行加密以后方可存入数据库中。
			MessageDigest md = MessageDigest.getInstance("SHA");
			byte[] raw = passWord.getBytes("UTF-8");
			byte[] hash = md.digest(raw);
			connection = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = connection.prepareStatement("INSERT INTO accounts (login,password,lastactive,accessLevel,lastIP, sdk, channelID, versionID, clientInfo,createTime) values(?,?,?,?,?,?,?,?,?,?)");
			statement.setString(1, userName);
			statement.setString(2, Base64.encodeBytes(hash));
			statement.setLong(3, System.currentTimeMillis());
			statement.setInt(4, 0);
			statement.setString(5, lastIp);
			statement.setInt(6, sdk);
			statement.setString(7, channelID);
			statement.setInt(8, clientVersion);
			statement.setString(9, clientInfo);
			statement.setLong(10, System.currentTimeMillis());
			statement.execute();
			statement.close();
			_log.info("创建账户:", Config.SERVER_ID, userName);
			result = true;
		} catch (Exception e) {
			_log.error("could not register account: " + userName + "!", Config.SERVER_ID, e);
		} finally {
			try {
				connection.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return result;
	}

	/**
	 * 注册帐户同步方法
	 * 
	 * @param userName
	 * @param passWord
	 * @param lastIp
	 * @param sdk
	 * @param channelID
	 * @param clientInfo
	 * @param clientVersion
	 * @return
	 */
	public synchronized int registerAccountSyn(String userName, String passWord, String lastIp, byte sdk, String channelID, String clientInfo, int clientVersion) {
		int result = -1;
		if (isAccountExists(userName)) {
			result = 0;
		} else if (registerAccount(userName, passWord, lastIp, sdk, channelID, clientInfo, clientVersion)) {
			result = 1;
		}

		return result;
	}

	/**
	 * 修改密码账户校验
	 * 
	 * @param userName
	 * @param passWord
	 * @return
	 */
	private byte checkAccount(String userName, String passWord) {
		byte result = -1;
		String _passWord;
		Connection connection = null;
		PreparedStatement statement = null;
		try {
			MessageDigest md = MessageDigest.getInstance("SHA");
			byte[] raw = passWord.getBytes("UTF-8");
			byte[] hash = md.digest(raw);
			connection = L2DatabaseFactory.getInstance().getConnection();
			statement = connection.prepareStatement("select login,password from accounts where login = ?");
			statement.setString(1, userName);
			ResultSet rs = statement.executeQuery();
			if (rs.next()) {
				result = ResponseChangePassWord.RESPONSE_ALLOK; //-- 帐号密码正常
				_passWord = rs.getString("password");
				if (_passWord != null) {
					if (!_passWord.equals(Base64.encodeBytes(hash))) {
						result = ResponseChangePassWord.RESPONSE_ACCOUNTORPASSWORD_ERROR; //-- 密码错误
					}
				} else {
					result = ResponseChangePassWord.RESPONSE_ACCOUNTORPASSWORD_ERROR; //-- 密码错误
				}
			} else
				result = ResponseChangePassWord.RESPONSE_ACCOUNTORPASSWORD_ERROR; //-- 用户不存在
			statement.close();
		} catch (Exception e) {
			_log.error("could not check is user exsits!", Config.SERVER_ID, e);
		} finally {
			try {
				if (connection != null)
					connection.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return result;
	}

	/**
	 * 用户修改密码
	 * 
	 * @param userName
	 * @param passWord
	 * @return
	 */
	public synchronized byte resetPassWord(String userName, String oldPassWord, String newPassWord) {
		byte result;
		Connection connection = null;
		PreparedStatement statement = null;
		result = checkAccount(userName, oldPassWord);
		if (result == ResponseChangePassWord.RESPONSE_ALLOK) {
			try {
				MessageDigest md = MessageDigest.getInstance("SHA");
				byte[] raw = newPassWord.getBytes("UTF-8");
				byte[] hash = md.digest(raw);
				connection = L2DatabaseFactory.getInstance().getConnection();
				statement = connection.prepareStatement("update accounts set password = ? where login = ?");
				statement.setString(1, Base64.encodeBytes(hash));
				statement.setString(2, userName);
				statement.executeUpdate();
				statement.close();
			} catch (Exception e) {
				_log.error("could not check is user exsits!", Config.SERVER_ID, e);
				result = ResponseChangePassWord.RESPONSE_NEWPASSWORD_ERROR; //-- 更新密码失败
			} finally {
				try {
					if (connection != null)
						connection.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		return result;
	}

	/**
	 * 修改密码账户校验
	 * 
	 * @param userName
	 * @param passWord
	 * @return
	 */
	private byte checkAccountAndPhoneNo(String userName, String passWord, String phoneNo) {
		byte result = -1;
		String _passWord;
		String _phoneNo;
		Connection connection = null;
		PreparedStatement statement = null;
		try {
			MessageDigest md = MessageDigest.getInstance("SHA");
			byte[] raw = passWord.getBytes("UTF-8");
			byte[] hash = md.digest(raw);
			connection = L2DatabaseFactory.getInstance().getConnection();
			statement = connection.prepareStatement("select login,password,telephone from accounts where login = ?");
			statement.setString(1, userName);
			ResultSet rs = statement.executeQuery();
			if (rs.next()) {
				result = ResponseBindAccount.RESPONSE_ALLOK; //-- 帐号密码正常
				_passWord = rs.getString("password");
				_phoneNo = rs.getString("telephone");
				if (_passWord != null) {
					if (!_passWord.equals(Base64.encodeBytes(hash))) {
						result = ResponseBindAccount.RESPONSE_ACCOUNTORPASSWORD_ERROR; //-- 密码错误
					} else {
						if (_phoneNo != null)
							result = ResponseBindAccount.RESPONSE_BINDED_ERROR; //-- 密码错误
					}
				} else {
					result = ResponseBindAccount.RESPONSE_ACCOUNTORPASSWORD_ERROR; //-- 密码错误
				}
			} else
				result = ResponseBindAccount.RESPONSE_ACCOUNTORPASSWORD_ERROR; //-- 用户不存在
			statement.close();
		} catch (Exception e) {
			_log.error("could not check is user exsits!", Config.SERVER_ID, e);
			result = ResponseBindAccount.RESPONSE_SYSTEM_ERROR;
		} finally {
			try {
				if (connection != null)
					connection.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return result;
	}

	/**
	 * 用户绑定手机号码
	 * 
	 * @param userName
	 * @param passWord
	 * @return
	 */
	public synchronized byte bindPhoneNo(String userName, String oldPassWord, String phoneNo) {
		byte result;
		Connection connection = null;
		PreparedStatement statement = null;
		result = checkAccountAndPhoneNo(userName, oldPassWord, phoneNo);
		if (result == ResponseBindAccount.RESPONSE_ALLOK) {
			try {
				connection = L2DatabaseFactory.getInstance().getConnection();
				statement = connection.prepareStatement("update accounts set telephone = ? where login = ?");
				statement.setString(1, phoneNo);
				statement.setString(2, userName);
				statement.executeUpdate();
				statement.close();
			} catch (Exception e) {
				_log.error("could not check is user exsits!", Config.SERVER_ID, e);
				result = ResponseBindAccount.RESPONSE_SYSTEM_ERROR; //-- 更新密码失败
			} finally {
				try {
					if (connection != null)
						connection.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		return result;
	}

	/**
	 * 更新账号信息
	 * 
	 * @param userName
	 * @param passWord
	 * @param lastIp
	 * @param sdk
	 * @param channelID
	 * @param clientInfo
	 * @param clientVersion
	 * @return
	 */
	public boolean resetAccountInfo(String userName, String passWord, String lastIp, byte sdk, String channelID, String clientInfo, int clientVersion) {
		boolean result = false;
		Connection connection = null;

		try {
			connection = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement stmt = connection.prepareStatement("update accounts set lastactive=?, lastIP=?, sdk =?, channelID=?, clientInfo=?, versionID=?  where login = ?");
			stmt.setLong(1, System.currentTimeMillis());
			stmt.setString(2, lastIp);
			stmt.setByte(3, sdk);
			stmt.setString(4, channelID);
			stmt.setString(5, clientInfo);
			stmt.setInt(6, clientVersion);
			stmt.setString(7, userName);
			stmt.execute();
			stmt.close();
			result = true;
		} catch (Exception e) {
			_log.error("could not reset account: " + userName + "!", Config.SERVER_ID, e);
		} finally {
			try {
				connection.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return result;
	}

	/**
	 * 主要功能是校验用户、口令的合法性，如果用户不存在则可以根据是否自动注册账户开关自动注册账户；同时鉴权用户账户是否被屏蔽。 鉴权成功情况下更新用户最后登陆时间以及登陆服务器等信息。 user name is not case sensitive any more 用户名不区分大小写敏感
	 * 
	 * @param user
	 * @param password
	 * @param address
	 * @return
	 */
	public boolean loginValid(String user, String password, Client client) {//检查用户名和密码是否正确
		boolean ok = false;
		InetAddress address = client.getInetAddress();

		// player disconnected meanwhile
		if (address == null) {
			return false;
		}

		Connection con = null;
		try {
			MessageDigest md = MessageDigest.getInstance("SHA");
			byte[] raw = password.getBytes("UTF-8");
			byte[] hash = md.digest(raw);

			byte[] expected = null;
			int access = 0;
			int lastServer = 1;

			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con.prepareStatement("SELECT password, accessLevel, lastServer FROM accounts WHERE login=?");
			statement.setString(1, user);
			ResultSet rset = statement.executeQuery();

			if (rset.next()) {
				expected = Base64.decode(rset.getString("password"));
				access = rset.getInt("accessLevel");
				lastServer = rset.getInt("lastServer");
				if (lastServer <= 0)
					lastServer = 1; // minServerId is 1 in Interlude
				if (_log.isDebugEnabled())
					_log.debug("account exists");
			}
			rset.close();
			statement.close();

			// if account doesnt exists
			if (expected == null) {//如果账户不存在
				if (Config.AUTO_CREATE_ACCOUNTS) {//如果账户不存在且可以自动注册用户，则自动注册用户，写数据库。
					statement = con.prepareStatement("INSERT INTO accounts (login,password,lastactive,accessLevel,lastIP) values(?,?,?,?,?)");
					statement.setString(1, user);
					statement.setString(2, Base64.encodeBytes(hash));
					statement.setLong(3, System.currentTimeMillis());
					statement.setInt(4, 0);
					statement.setString(5, address.getHostAddress());
					statement.execute();
					statement.close();
					_log.info("created new account for " + user + " IP:" + address.getHostAddress(), Config.SERVER_ID, "自动创建账户成功");
					return true;
				}
				_log.warn("account missing for user " + user + " IP:" + address.getHostAddress(), Config.SERVER_ID, "无效的账户名");
				return false;
			} else {
				if (access < 0) {//-- 账户存在情况下判断是否是被屏蔽账户。
					client.setAccessLevel(access);
					return false;
				}

				//用户口令检查，直接比对hash值。
				ok = true;
				for (int i = 0; i < expected.length; i++) {
					if (hash[i] != expected[i]) {
						ok = false;
						break;
					}
				}
			}
		} catch (Exception e) {
			_log.error("Could not check password:", Config.SERVER_ID, e);
			ok = false;
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}

		if (!ok) {
			if (Config.LOG_LOGIN_CONTROLLER) //-- 是否在日志记录中记录登录情况
				_log.warn("'" + user + "' IP:" + address.getHostAddress(), Config.SERVER_ID, "账户登录校验错误");

			FailedLoginAttempt failedAttempt = _hackProtection.get(address);
			int failedCount;
			if (failedAttempt == null) {
				_hackProtection.put(address, new FailedLoginAttempt(address, password));
				failedCount = 1;
			} else {
				failedAttempt.increaseCounter(password);
				failedCount = failedAttempt.getCount();
			}

			if (failedCount >= Config.LOGIN_TRY_BEFORE_BAN) {
				_log.info("Banning:'" + address.getHostAddress() + "' for " + Config.LOGIN_BLOCK_AFTER_BAN + " seconds due to " + failedCount + " invalid user/pass attempts", Config.SERVER_ID, user);
				this.addBanForAddress(address, Config.LOGIN_BLOCK_AFTER_BAN * 1000);
			}
		} else {
			_hackProtection.remove(address);
			if (Config.LOG_LOGIN_CONTROLLER)
				_log.info("'" + user + "' IP:" + address.getHostAddress(), Config.SERVER_ID, "账户登录校验通过");
		}

		return ok;
	}

	/**
	 * 判断账户是否是屏蔽账户
	 * 
	 * @param user
	 *            账户
	 * @return
	 */
	public boolean loginBanned(String user) {
		boolean ok = false;

		Connection con = null;
		try {
			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con.prepareStatement("SELECT accessLevel FROM accounts WHERE login=?");
			statement.setString(1, user);
			ResultSet rset = statement.executeQuery();
			if (rset.next()) {
				int accessLevel = rset.getInt(1);
				if (accessLevel < 0)
					ok = true;
			}
			rset.close();
			statement.close();
		} catch (Exception e) {
			// digest algo not found ??
			// out of bounds should not be possible
			_log.error("could not check ban state:", Config.SERVER_ID, e);
			ok = false;
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}

		return ok;
	}

	/**
	 * 
	 * @author
	 * 
	 */
	class FailedLoginAttempt {
		//private InetAddress _ipAddress;
		private int _count;

		private long _lastAttempTime;

		private String _lastPassword;

		public FailedLoginAttempt(InetAddress address, String lastPassword) {
			//_ipAddress = address;
			_count = 1;
			_lastAttempTime = System.currentTimeMillis();
			_lastPassword = lastPassword;
		}

		public void increaseCounter(String password) {
			if (!_lastPassword.equals(password)) {
				// check if theres a long time since last wrong try
				if (System.currentTimeMillis() - _lastAttempTime < 300 * 1000) {
					_count++;
				} else {
					// restart the status
					_count = 1;

				}
				_lastPassword = password;
				_lastAttempTime = System.currentTimeMillis();
			} else
			//trying the same password is not brute force
			{
				_lastAttempTime = System.currentTimeMillis();
			}
		}

		public int getCount() {
			return _count;
		}
	}

	public class BanInfo {
		private InetAddress _ipAddress;

		// Expiration
		private long _expiration;

		public BanInfo(InetAddress ipAddress, long expiration) {
			_ipAddress = ipAddress;
			_expiration = expiration;
		}

		public InetAddress getAddress() {
			return _ipAddress;
		}

		public boolean hasExpired() {
			return System.currentTimeMillis() > _expiration && _expiration > 0;
		}
	}

	/**
	 * 内部清除服务类
	 * 
	 * @author Administrator
	 * 
	 */
	class PurgeThread extends Thread {//清除服务，清除那些连接了没有授权的和授权了却不登陆的account
		@Override
		public void run() {
			for (;;) {

				Iterator<LoginClient> i = _clients.iterator();

				while (i.hasNext()) {
					LoginClient client = i.next();
					if (client.getConnectionStartTime() + LOGIN_TIMEOUT <= System.currentTimeMillis()) {
						//						_clients.remove(client);
						client.close(LoginFailReason.REASON_ACCESS_FAILED.getLoginFail());
					}
				}

				for (Map.Entry<String, Client> c : _loginServerClients.entrySet()) {
					Client client = c.getValue();
					if (client.getConnectionStartTime() + LOGIN_TIMEOUT <= System.currentTimeMillis()) {
						_loginServerClients.remove(c.getKey());//socket关闭的时候会清除，但是http不会
					}
				}

				try {
					Thread.sleep(2 * LOGIN_TIMEOUT);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public boolean isValidName(String text) {
		if (!Util.isAlphaNumeric(text))
			return false;

		if (NAME_PATTERN == null)
			return false;

		Matcher regexp = NAME_PATTERN.matcher(text);
		if (!regexp.matches()) {
			return false;
		}
		return true;
	}

	public boolean isValidPassword(String text) {
		if (!Util.isAlphaNumeric(text))
			return false;

		if (PASSWORD_PATTERN == null)
			return false;

		Matcher regexp = PASSWORD_PATTERN.matcher(text);
		if (!regexp.matches()) {
			return false;
		}
		return true;
	}

	public boolean isValidPhoneNo(String text) {
		if (!Util.isAlphaNumeric(text))
			return false;

		if (PHONENO_TEMPLATE == null) {
			PHONENO_TEMPLATE = Pattern.compile(Config.ACCOUNT_PHONENO_TEMPLATE);
		}

		Matcher regexp = PHONENO_TEMPLATE.matcher(text);
		if (!regexp.matches()) {
			return false;
		}
		return true;
	}

	public boolean setNamePattern(String pattern) {
		if (pattern == null)
			return false;
		try {
			NAME_PATTERN = Pattern.compile(pattern);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	public boolean setPasswordPattern(String pattern) {
		if (pattern == null)
			return false;
		try {
			PASSWORD_PATTERN = Pattern.compile(pattern);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	public final void setSVN(Svn res) {
		_svn = res;
	}

	public final Svn getSVN() {
		return _svn;
	}
}
